이번 튜토리얼에서는 인공신경망 모델을 이용해서 영화 리뷰(review) 텍스트를 긍정(positive) 또는 부정(negative)으로 분류합니다. 이 예제는 머신러닝에서 널리 사용되는 이진(binary) 즉, 클래스(class)가 두 개인 분류 문제입니다.
여기에서는 인터넷 영화 데이터베이스(Internet Movie Database)에서 수집한 50,000개의 영화 리뷰 텍스트를 담은 IMDB 데이터셋을 사용하겠습니다. 총 50,000개의 리뷰 데이터 중 25,000개 리뷰는 훈련용으로, 25,000개는 테스트용으로 나누어져 있습니다. 나누어져 있는 훈련 세트와 테스트 세트의 클래스는 긍정적인 리뷰와 부정적인 리뷰의 개수가 동일하기 때문에 균형이 잡혀 있다고 할 수 있습니다.
이번 예제에서는 전이 학습 라이브러리이자 플랫폼인 텐서플로우 허브(Tensoorflow Hub)를 사용합니다.
# 필요한 라이브러리 임포트
import warnings
warnings.simplefilter('ignore')
import numpy as np
import tensorflow as tf
import tensorflow_hub as hub
# Tensorflow의 버전을 확인합니다
print("버전: ", tf.__version__)
# 즉시 실행 모드는 즉시 실행은 그래프를 생성하지 않고 함수를 바로 실행하는 명령형 프로그래밍 환경입니다.
print("즉시 실행 모드: ", tf.executing_eagerly())
# Tensorflow Hub는 머신러닝 모델의 재사용 가능한 부분을 게시, 검색, 소비하기 위한 라이브러리입니다.
print("허브 버전: ", hub.__version__)
IMDB 데이터셋은 텐서플로 데이터셋(TensorFlow datasets)에 포함되어 있기 때문에 다음 코드를 이용하여 IMDB 데이터셋을 다운로드 할 수 있습니다.:
import tensorflow_datasets as tfds
# 훈련 세트를 검증 데이터 사용을 위하여 6대 4로 나눕니다.
# 따라서 결국 훈련에 15,000개 샘플, 검증에 10,000개 샘플, 테스트에 25,000개 샘플을 사용하게 됩니다.
train_validation_split = tfds.Split.TRAIN.subsplit([6, 4])
(train_data, validation_data), test_data = tfds.load(
name="imdb_reviews",
split=(train_validation_split, tfds.Split.TEST),
as_supervised=True)
잠시 데이터 형태를 알아 봅시다. 이 데이터셋의 샘플은 전처리된 정수 배열입니다. 각 정수는 영화 리뷰에 나오는 단어를 나타냅니다. 레이블(label)은 정수 0 또는 1입니다. 0은 부정적인 리뷰 이고 1은 긍정적인 리뷰입니다.
처음 10개의 샘플을 출력해 보겠습니다.
train_examples_batch, train_labels_batch = next(iter(train_data.batch(10)))
train_examples_batch
처음 10개의 레이블도 출력해 보겠습니다.
train_labels_batch
신경망은 층을 쌓아서 만듭니다. 여기에는 세 개의 중요한 구조적 결정이 필요합니다:
이 예제의 입력 데이터는 문장으로 구성됩니다. 예측할 레이블은 0 또는 1입니다.
텍스트를 표현하는 한 가지 방법은 문장을 임베딩(embedding) 벡터로 바꾸는 것입니다. 임베딩은 텍스트를 숫자로 이루어진 벡터로 바꾼 결과 또는 그 과정 전체를 말합니다. 그러면 첫 번째 층으로 사전 훈련(pre-trained)된 텍스트 임베딩을 사용할 수 있습니다. 여기에는 다음과 같은 장점이 있습니다.
이번 예제는 텐서플로우 허브에 있는 사전 훈련된 텍스트 임베딩 모델인 google/tf2-preview/gnews-swivel-20dim/1을 사용합니다.
먼저 문장을 임베딩시키기 위해 텐서플로 허브 모델을 사용하는 케라스(keras) 층을 만들어 봅시다.
그 다음 몇 개의 샘플을 입력하여 테스트해 보겠습니다.
입력 텍스트의 길이에 상관없이 임베딩의 출력 크기는 (num_examples, embedding_dimension)
가 됩니다.
embedding = "https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1"
hub_layer = hub.KerasLayer(embedding, input_shape=[],
dtype=tf.string, trainable=True)
hub_layer(train_examples_batch[:3])
이제 전체 모델을 만들어 보겠습니다:
model = tf.keras.Sequential()
model.add(hub_layer)
model.add(tf.keras.layers.Dense(16, activation='relu'))
model.add(tf.keras.layers.Dense(1, activation='sigmoid'))
model.summary()
순서대로 층을 쌓아 분류기(classifier)를 만듭니다:
첫 번째 층은 텐서플로 허브 층입니다. 이 층은 사전 훈련된 모델을 사용하여 하나의 문장을 임베딩 벡터로 매핑합니다. 여기서 사용하는 사전 훈련된 텍스트 임베딩 모델(google/tf2-preview/gnews-swivel-20dim/1)은 하나의 문장을 토큰(token)으로 나누고 각 토큰의 임베딩을 연결하여 반환합니다. 최종 차원은 (num_examples, embedding_dimension)
입니다.
이 고정 크기의 출력 벡터는 16개의 은닉 유닛(hidden unit)을 가진 완전 연결 층(Dense
)으로 주입됩니다.
마지막 층은 하나의 출력 노드를 가진 완전 연결 층입니다. sigmoid
활성화 함수를 사용하므로 확률 또는 신뢰도 수준을 표현하는 0~1 사이의 실수가 출력됩니다.
이제 모델을 컴파일합니다.
모델이 훈련하려면 손실 함수(loss function) 과 옵티마이저(optimizer) 가 필요합니다. 이 예제는 이진 분류 문제이고 모델이 확률을 출력하므로(출력층의 유닛이 하나이고 sigmoid
활성화 함수를 사용합니다), binary_crossentropy
손실 함수를 사용하겠습니다.
물론 다른 손실 함수를 선택할 수 없는 것은 아닙니다. 예를 들어 mean_squared_error
를 선택할 수 있습니다.
하지만 일반적으로 binary_crossentropy
가 확률을 다루는데 적합합니다. 이 함수는 확률 분포 간의 거리를 측정합니다. 여기에서는 정답인 타깃 분포와 예측 분포 사이의 거리입니다.
이제 모델이 사용할 옵티마이저와 손실 함수를 설정해 봅시다:
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])
이 모델을 512개의 샘플로 이루어진 미니배치(mini-batch)에서 20번의 에포크(epoch) 동안 훈련합니다. x_train
과 y_train
텐서에 있는 모든 샘플에 대해 20번 반복한다는 뜻입니다. 훈련하는 동안 10,000개의 검증 세트에서 모델의 손실과 정확도를 모니터링합니다:
history = model.fit(train_data.shuffle(10000).batch(512),
epochs=20,
validation_data=validation_data.batch(512),
verbose=1)
모델의 성능을 확인해 봅시다. 손실(loss, 오차를 나타내는 숫자이므로 낮을수록 좋습니다) 과 정확도(accuracy) 두 개의 값이 반환됩니다.
results = model.evaluate(test_data.batch(512), verbose=2)
for name, value in zip(model.metrics_names, results):
print("%s: %.3f" % (name, value))
학습 결과, 약 87% 정도의 정확도를 달성했습니다.
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#@title MIT License
#
# Copyright (c) 2017 François Chollet
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.